﻿using System;
using System.Collections.Generic;
using Alone.GameObjects;
using Alone.GameObjects.Enemy;
using Alone.GameObjects.Objects;
using AloneLibrary.GameObjects;
using AloneLibrary.Services.Camera;
using AloneLibrary.Services.Sound;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace Alone.Handlers
{
	public interface IEnvironmentHandler
	{
		List<StaticGameObject> GetObjectList
		{
			get;
		}
		HeightMapInfo HeightMap
		{
			get;
		}
	}

	public class EnvironmentHandler : DrawableGameComponent, IEnvironmentHandler
	{
		private Terrain terrain;
		private Model terrainModel;
		private HeightMapInfo heightmap;
		private List<StaticGameObject> objects = new List<StaticGameObject>();
		private ICameraHandler camera;
		private IPlayerHandler playerHandler;
		private IEnemyHandler enemyHandler;
		private IBallisticsHandler ballisticsHandler;
		private Random rand = new Random();
		private List<Enemy> enemies;
		private Vector3 position;
		private int timeSinceLastInterval;
        private ISoundManager soundHandler;

        /// <summary>
        /// Gets the height map.
        /// </summary>
        /// <value>The height map.</value>
        public HeightMapInfo HeightMap {
            get { return this.heightmap; }
        }

        /// <summary>
        /// Gets the object list.
        /// </summary>
        /// <returns></returns>
        public List<StaticGameObject> GetObjectList {
            get { return objects; }
        }

		/// <summary>
		/// Initializes a new instance of the <see cref="EnvironmentHandler"/> class.
		/// </summary>
		/// <param name="game">The game.</param>
		/// <param name="playerHandler">The player handler.</param>
		public EnvironmentHandler(Game game)
			: base(game)
		{
			Game.Services.AddService(typeof(IEnvironmentHandler), this);
		}

		/// <summary>
		/// Initializes the component. Override this method to load any non-graphics resources and query for any required services.
		/// </summary>
		public override void Initialize()
		{
			camera = (ICameraHandler)Game.Services.GetService(typeof(ICameraHandler));
			enemyHandler = (IEnemyHandler)Game.Services.GetService(typeof(IEnemyHandler));
			position = new Vector3((float)plusOrMinus(rand.Next(3000, 6000)), 50, (float)plusOrMinus(rand.Next(3000, 6000)));
			ballisticsHandler = (IBallisticsHandler)Game.Services.GetService(typeof(IBallisticsHandler));
			playerHandler = (IPlayerHandler)Game.Services.GetService(typeof(IPlayerHandler));
            soundHandler = (ISoundManager)Game.Services.GetService(typeof(ISoundManager));

			base.Initialize();
		}

		/// <summary>
		/// Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
		/// </summary>
		protected override void LoadContent()
		{
			camera = (ICameraHandler)Game.Services.GetService(typeof(ICameraHandler));

			terrainModel = Game.Content.Load<Model>(@"Textures\aloneterrain");
			terrain = new Terrain(terrainModel, Game.Content.Load<Texture2D>(@"Textures\grass"), Game.Content.Load<Effect>(@"Effects\AloneEffect"));
			heightmap = terrainModel.Tag as HeightMapInfo;
			
			addEnvironment();

			base.LoadContent();
		}


		/// <summary>
		/// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
		/// </summary>
		/// <param name="gameTime">Time elapsed since the last call to Update</param>
		public override void Update(GameTime gameTime)
		{
			if (this.Visible)
			{
				LocalPlayer player = playerHandler.Player;
				enemies = enemyHandler.Enemies;

				foreach (StaticGameObject obj in objects)
				{
                    if (player.isColliding(obj)) //do this first, for efficency, maybe...
					{
                        //disable the ones needed to be disabled
                        bool[] collisionDirections = player.GetCollisionDirections(obj);

                        player.DisabledForward = collisionDirections[0];
                        player.DisabledBackward = collisionDirections[1];
                        player.DisabledLeft = collisionDirections[2];
                        player.DisabledRight = collisionDirections[3];
                    }

                    foreach (Enemy enemy in enemies) {
                        if (enemy.isColliding(obj)) {
                            enemy.DoValidTurn(obj);
                        }
                    }

                    if(obj is Ammo) obj.Rotation = new Vector2(obj.Rotation.Y + .1f, obj.Rotation.X);
				}
			}
			checkPickedAmmo(gameTime);
			base.Update(gameTime);
		}

        /// <summary>
        /// Pluses the or minus.
        /// </summary>
        /// <param name="ran">The ran.</param>
        /// <returns></returns>
		private int plusOrMinus(int ran)
		{
			double next = rand.NextDouble();
			if (next < 0.5)
				return ran;
			return -1 * ran;
		}

		/// <summary>w
		/// Adds the environment.
		/// </summary>
		private void addEnvironment()
		{

            Model weaponModel = Game.Content.Load<Model>(@"Models\player\weapon");
            Effect weaponEffect = Game.Content.Load<Effect>(@"Effects\AloneEffect");
            Vector3 pos;

            for(int i = 0; i < 20; i++) {

                pos = new Vector3(plusOrMinus(rand.Next(3200)), 0, plusOrMinus(rand.Next(3200)));
                pos.Y = heightmap.GetHeight(pos) + 20f;

                objects.Add(new Ammo(
						weaponModel,
						weaponEffect,
						pos,
						Vector2.Zero,
						4.0f
					));
            }
		}

        /// <summary>
        /// Checks the picked ammo.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
		public void checkPickedAmmo(GameTime gameTime)
		{
			Ammo obj;
			timeSinceLastInterval = (int)gameTime.TotalGameTime.TotalSeconds;

            for (int i = 0; i < objects.Count - 1; i++)
            {
                if (objects[i] is Ammo)
                {
                    obj = (Ammo)objects[i];

                    if (!obj.Available && timeSinceLastInterval >= (obj.UpdateAmmoInterval + obj.HiddenSince))
                    {
                        obj.Available = true;
                    }
                    else if (playerHandler.Player.isColliding(obj))
                    {
                        playerHandler.Weapon.Ammo += 5;

                        ballisticsHandler.Hud.updateFontSprite("ammo", "ammo", playerHandler.Weapon.Ammo.ToString());

                        obj.Available = false;
                        obj.HiddenSince = timeSinceLastInterval;
                    }
                }
            }
		}

		/// <summary>
		/// Called when the DrawableGameComponent needs to be drawn. Override this method with component-specific drawing code. Reference page contains links to related conceptual articles.
		/// </summary>
		/// <param name="gameTime">Time passed since the last call to Draw.</param>
		public override void Draw(GameTime gameTime)
		{
            if(this.playerHandler.Player.viewFrustum.Intersects(terrain.BoundingBox))
			    terrain.Draw(gameTime, camera);

            foreach (GameObject go in objects)
            {
                if (go is Ammo)
                {
                    if ((go as Ammo).Available)
                    {
                        if(this.playerHandler.Player.viewFrustum.Intersects(go.BoundingBox))
                            go.Draw(gameTime, camera);
                    }
                }
                else
                {
                    if(this.playerHandler.Player.viewFrustum.Intersects(go.BoundingBox))
                        go.Draw(gameTime, camera);
                }
            }
			base.Draw(gameTime);
		}
	}
}
